#!/bin/sh
#
# This shell script checks the TLS certificates and options needed
# for the secure client/server support of libvirt as documented at
# https://libvirt.org/kbase/tlscerts.html
#
# Copyright (C) 2009-2013 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.  If not, see
# <http://www.gnu.org/licenses/>.
#
# Daniel Veillard <veillard@redhat.com>
#

case $1 in
  -h | --h | --he | --hel | --help)
    cat <<EOF
Usage:
  $0 [OPTION]

Options:
  -h | --help        Display program help
  -V | --version     Display program version
EOF
    exit ;;
  -V | --v | --ve | --ver | --vers | --versi | --versio | --version)
    cat <<EOF
$0 (libvirt) @VERSION@
EOF
    exit ;;
  --) shift ;;
  -) # Not an option but an argument; it gets rejected later
    ;;
  -*)
    echo "$0: unrecognized option '$1'" >&2
    exit 1 ;;
esac

if test $# != 0; then
    echo "$0: unrecognized argument '$1'" >&2
    exit 1
fi

USER=`who am i | awk '{ print $1 }'`
SERVER=1
CLIENT=1
PORT=16514
#
# First get certtool
#
CERTOOL=`which certtool 2>/dev/null`
if [ ! -x "$CERTOOL" ]
then
    echo "Could not locate the certtool program"
    echo "make sure the gnutls-utils (or gnutls-bin) package is installed"
    exit 1
fi
echo Found "$CERTOOL"

#
# Check the directory structure
#
SYSCONFDIR="@sysconfdir@"
PKI="$SYSCONFDIR/pki"
INITCONFDIR="@initconfdir@"
if [ ! -d "$PKI" ]
then
    echo the $PKI directory is missing, it is usually
    echo installed as part of the filesystem or openssl packages
    exit 1
fi

if [ ! -r "$PKI" ]
then
    echo the $PKI directory is not readable by $USER
    echo "as root do: chmod a+rx $PKI"
    exit 1
fi
if [ ! -x "$PKI" ]
then
    echo the $PKI directory is not listable by $USER
    echo "as root do: chmod a+rx $PKI"
    exit 1
fi

CA="$PKI/CA"
if [ ! -d "$CA" ]
then
    echo the $CA directory is missing, it is usually
    echo installed as part of the or openssl package
    exit 1
fi

if [ ! -r "$CA" ]
then
    echo the $CA directory is not readable by $USER
    echo "as root do: chmod a+rx $CA"
    exit 1
fi
if [ ! -x "$CA" ]
then
    echo the $CA directory is not listable by $USER
    echo "as root do: chmod a+rx $CA"
    exit 1
fi

LIBVIRT="$PKI/libvirt"
if [ ! -d "$LIBVIRT" ]
then
    echo the $LIBVIRT directory is missing, it is usually
    echo installed by the libvirt package
    echo "as root do: mkdir -m 755 $LIBVIRT ; chown root:root $LIBVIRT"
    exit 1
fi

if [ ! -r "$LIBVIRT" ]
then
    echo the $LIBVIRT directory is not readable by $USER
    echo "as root do: chown root:root $LIBVIRT ; chmod 755 $LIBVIRT"
    exit 1
fi
if [ ! -x "$LIBVIRT" ]
then
    echo the $LIBVIRT directory is not listable by $USER
    echo "as root do: chown root:root $LIBVIRT ; chmod 755 $LIBVIRT"
    exit 1
fi

LIBVIRTP="$LIBVIRT/private"
if [ ! -d "$LIBVIRTP" ]
then
    echo the $LIBVIRTP directory is missing, it is usually
    echo installed by the libvirt package
    echo "as root do: mkdir -m 755 $LIBVIRTP ; chown root:root $LIBVIRTP"
    exit 1
fi

if [ ! -r "$LIBVIRTP" ]
then
    echo the $LIBVIRTP directory is not readable by $USER
    echo "as root do: chown root:root $LIBVIRTP ; chmod 755 $LIBVIRTP"
    exit 1
fi
if [ ! -x "$LIBVIRTP" ]
then
    echo the $LIBVIRTP directory is not listable by $USER
    echo "as root do: chown root:root $LIBVIRTP ; chmod 755 $LIBVIRTP"
    exit 1
fi

#
# Now check the certificates
# First the CA certificate
#
if [ ! -f "$CA/cacert.pem" ]
then
    echo the CA certificate $CA/cacert.pem is missing while it
    echo should be installed on both client and servers
    echo "see https://libvirt.org/kbase/tlscerts.html#setting-up-a-certificate-authority-ca"
    echo on how to install it
    exit 1
fi
if [ ! -r "$CA/cacert.pem" ]
then
    echo the CA certificate $CA/cacert.pem is not readable by $USER
    echo "as root do: chmod 644 $CA/cacert.pem"
    exit 1
fi
sed_get_org='/Issuer:/ {
  s/.*Issuer:.*CN=//
  s/,.*//
  p
}'
ORG=`"$CERTOOL" -i --infile "$CA/cacert.pem" | sed -n "$sed_get_org"`
if [ "$ORG" = "" ]
then
    echo the CA certificate $CA/cacert.pem does not define the organization
    echo it should probably regenerated
    echo "see https://libvirt.org/kbase/tlscerts.html#setting-up-a-certificate-authority-ca"
    echo on how to regenerate it
    exit 1
fi
echo Found CA certificate $CA/cacert.pem for $ORG

# Second the client certificates

if [ -f "$LIBVIRT/clientcert.pem" ]
then
    if [ ! -r "$LIBVIRT/clientcert.pem" ]
    then
        echo Client certificate $LIBVIRT/clientcert.pem should be world readable
        echo "as root do: chown root:root $LIBVIRT/clientcert.pem ; chmod 644 $LIBVIRT/clientcert.pem"
    else
        C_ORG=`"$CERTOOL" -i --infile "$LIBVIRT/clientcert.pem" | grep Subject: | sed 's+.*O=\([^,]*\).*+\1+'`
        if [ "$ORG" != "$C_ORG" ]
        then
            echo The CA certificate and the client certificate do not match
            echo CA organization: $ORG
            echo Client organization: $C_ORG
        fi
        CLIENT=`"$CERTOOL" -i --infile "$LIBVIRT/clientcert.pem" | grep Subject: | sed 's+.*CN=\(.[^,]*\).*+\1+'`
        echo Found client certificate $LIBVIRT/clientcert.pem for $CLIENT
        if [ ! -e "$LIBVIRTP/clientkey.pem" ]
        then
            echo Missing client private key $LIBVIRTP/clientkey.pem
        else
            echo Found client private key $LIBVIRTP/clientkey.pem
            OWN=`ls -l "$LIBVIRTP/clientkey.pem" | awk '{ print $3 }'`
            # The substr($1, 1, 10) gets rid of acl and xattr markers
            MOD=`ls -l "$LIBVIRTP/clientkey.pem" | awk '{ print substr($1, 1, 10) }'`
            if [ "$OWN" != "root" ]
            then
                echo The client private key should be owned by root
                echo "as root do: chown root $LIBVIRTP/clientkey.pem"
            fi
            if [ "$MOD" != "-rw-r--r--" ]
            then
                echo The client private key need to be read by client tools
                echo "as root do: chmod 644 $LIBVIRTP/clientkey.pem"
            fi
        fi

    fi
else
    echo Did not find "$LIBVIRT/clientcert.pem" client certificate
    echo The machine cannot act as a client
    echo "see https://libvirt.org/kbase/tlscerts.html#issuing-client-certificates"
    echo on how to regenerate it
    CLIENT=0
fi

# Third the server certificates

if [ -f "$LIBVIRT/servercert.pem" ]
then
    if [ ! -r "$LIBVIRT/servercert.pem" ]
    then
        echo Server certificate $LIBVIRT/servercert.pem should be world readable
        echo "as root do: chown root:root $LIBVIRT/servercert.pem ; chmod 644 $LIBVIRT/servercert.pem"
    else
        S_ORG=`"$CERTOOL" -i --infile "$LIBVIRT/servercert.pem" | grep Subject: | sed 's+.*O=\([^,]*\).*+\1+'`
        if [ "$ORG" != "$S_ORG" ]
        then
            echo The CA certificate and the server certificate do not match
            echo CA organization: $ORG
            echo Server organization: $S_ORG
        fi
        S_HOST=`"$CERTOOL" -i --infile "$LIBVIRT/servercert.pem" | grep Subject: | sed 's+.*CN=\([^,]*\).*+\1+'`
        if test "$S_HOST" != "`hostname -s`" && test "$S_HOST" != "`hostname`"
        then
            echo The server certificate does not seem to match the host name
            echo hostname: '"'`hostname`'"'
            echo Server certificate CN: '"'$S_HOST'"'
        fi
        echo Found server certificate $LIBVIRT/servercert.pem for $S_HOST
        if [ ! -e "$LIBVIRTP/serverkey.pem" ]
        then
            echo Missing server private key $LIBVIRTP/serverkey.pem
        else
            echo Found server private key $LIBVIRTP/serverkey.pem
            OWN=`ls -l "$LIBVIRTP/serverkey.pem" | awk '{ print $3 }'`
            # The substr($1, 1, 10) gets rid of acl and xattr markers
            MOD=`ls -l "$LIBVIRTP/serverkey.pem" | awk '{ print substr($1, 1, 10) }'`
            if [ "$OWN" != "root" ]
            then
                echo The server private key should be owned by root
                echo "as root do: chown root $LIBVIRTP/serverkey.pem"
            fi
            if [ "$MOD" != "-rw-------" ]
            then
                echo The server private key need to be read only by root
                echo "as root do: chmod 600 $LIBVIRTP/serverkey.pem"
            fi
        fi

    fi
else
    echo Did not find $LIBVIRT/servercert.pem server certificate
    echo The machine cannot act as a server
    echo "see https://libvirt.org/kbase/tlscerts.html#issuing-server-certificates"
    echo on how to regenerate it
    SERVER=0
fi

if [ "$SERVER" = "1" ]
then
    if [ -r "$INITCONFDIR"/libvirtd ]
    then
        if grep "^LIBVIRTD_ARGS.*--listen" "$INITCONFDIR"/libvirtd \
            >/dev/null 2>&1
        then
            :
        else
            echo Make sure "$INITCONFDIR"/libvirtd is setup to listen to
            echo TCP/IP connections and restart the libvirtd service
        fi
    fi
    if [ -r "$INITCONFDIR"/iptables ]
    then
        if grep "$PORT" "$INITCONFDIR"/iptables >/dev/null 2>&1
        then
            :
        else
            echo Make sure "$INITCONFDIR"/iptables is setup to allow
            echo incoming TCP/IP connections on port $PORT and
            echo restart the iptables service
        fi
    fi
fi


exit 0
